#include <iostream>
#include <cstring>
#include <cassert>
#include <utility>
#include <string>

// #define PRINT_INFO

namespace stl_self
{
    class string
    {
        friend std::ostream &operator<<(std::ostream &out, const string &s);
        // friend std::istream& operator>>(std::istream& in, const string& s);

    private:
        char *_str;
        size_t _size;
        size_t _capacity;
        static const size_t npos;

    public:
        typedef char *iterator;
        typedef const char *const_iterator;

    public:
        iterator begin() { return _str; }
        iterator end() { return _str + _size; }
        const_iterator begin() const { return _str; }
        const_iterator end() const { return _str + _size; }

    public:
        string(const char *s = "")
            : _size(strlen(s))
        {
            _capacity = _size == 0 ? 4 : _size;
            _str = new char[_capacity + 1];
            strcpy(_str, s);
#ifdef PRINT_INFO
            std::cout << "string(const char* s = \"\")" << std::endl;
#endif
        }

        string(const string &s)
            : _size(s._size), _capacity(s._capacity)
        {
            _str = new char[s._capacity + 1];
            strcpy(_str, s._str);
#ifdef PRINT_INFO
            std::cout << "string(const string& s)" << std::endl;
#endif
        }

        string(string &&s)
            : _str(nullptr), _size(0), _capacity(0)
        {
            swap(s);
#ifdef PRINT_INFO
            std::cout << "string(string&& s)" << std::endl;
#endif
        }

        ~string()
        {
            if (_str != nullptr)
            {
                delete[] _str;
                _str = nullptr;
                _size = _capacity = 0;
            }
#ifdef PRINT_INFO
            std::cout << " ~string()" << std::endl;
#endif
        }

        size_t size() const { return _size; }
        size_t capacity() const { return _capacity; }
        const char *c_str() const { return _str; }
        void clear()
        {
            _str[0] = '\0';
            _size = 0;
        }

        void reserve(size_t n)
        {
            if (n > _capacity)
            {
                char *tmp = new char[n + 1];
                strcpy(tmp, _str);
                delete[] _str;
                _str = tmp;
                _capacity = n;
            }
        }

        void resize(size_t n)
        {
            if (n < _size)
            {
                _size = n;
                _str[n] = '\0';
            }
            else
            {
                if (n > _capacity) // 扩容
                {
                    reserve(n);
                }

                // _size <= n <= _capacity
                size_t index = _size;
                while (index < n)
                {
                    _str[index++] = '\0';
                }
                _size = n;
                _str[_size] = '\0';
            }
        }

        string &insert(size_t pos, char c)
        {
            assert(pos <= _size);
            if (_size + 1 > _capacity)
            {
                reserve(_capacity * 2);
            }
            // a b c d e f
            // end = _size = 6
            size_t end = _size + 1;
            while (end > pos)
            {
                _str[end] = _str[end - 1];
                --end;
            }
            _str[pos] = c;
            ++_size;
            return *this;
        }

        string &insert(size_t pos, const char *str)
        {
            assert(pos <= _size);
            size_t len = strlen(str);
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }
            size_t end = _size + len;
            while (end > pos + len - 1)
            {
                _str[end] = _str[end - len];
                --end;
            }
            strncpy(_str + pos, str, len);
            _size = _size + len;
            return *this;
        }

        string &erase(size_t pos, size_t len = npos)
        {
            assert(pos < _size);
            if (len == npos || pos + len > _capacity)
            {
                _str[pos] = '\0';
                _size = pos;
            }
            else
            {
                strcpy(_str + pos, _str + pos + len);
                _size = _size - len;
            }
            return *this;
        }

        void push_back(char c) { insert(_size, c); }
        void append(const char *str) { insert(_size, str); }
        string &operator+=(const char *str)
        {
            append(str);
            return *this;
        }

        size_t find(const char *str, size_t pos = 0)
        {
            assert(pos < _size);
            char *ret = strstr(_str + pos, str);
            if (ret == nullptr)
                return npos;
            else
                return ret - _str;
        }

        char &operator[](size_t pos)
        {
            assert(pos < _size);
            return _str[pos];
        }
        const char &operator[](size_t pos) const
        {
            assert(pos < _size);
            return _str[pos];
        }
        string &operator=(const string &s)
        {
            if (this != &s)
            {
                char *tmp = new char[s._capacity + 1];
                strcpy(tmp, s._str);
                delete[] _str;
                _str = tmp;
                _size = s._size;
                _capacity = s._capacity;
            }
#ifdef PRINT_INFO
            std::cout << "string &operator=(const string &s)" << std::endl;
#endif

            return *this;
        }

        string &operator=(string &&s)
        {
            if (this != &s)
            {
                swap(s);
            }
#ifdef PRINT_INFO
            std::cout << "string& operator=(string&& s)" << std::endl;
#endif

            return *this;
        }

        void swap(string &s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }
    };

    const size_t string::npos = -1;

    std::ostream &operator<<(std::ostream &out, const string &s)
    {
        for (auto &i : s)
        {
            out << i << " ";
        }
        return out;
    }

} // namespace my_stl

stl_self::string test()
{
    stl_self::string ss("edge");
    return ss;
}
